/*
 * Copyright (c) gitee.com/jiandong001.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */


import { ViewManagerPropertyUpdater } from './ViewManagerPropertyUpdater';
import { ReactStylesDiffMap } from './ReactStylesDiffMap';
import ArrayList from '@ohos.util.ArrayList';

import { getYogaEdgeFromNumber } from '../../yoga/YogaEdge';
import { YogaMeasureFunction } from '../../yoga/YogaMeasureFunction';
import { YogaBaselineFunction } from '../../yoga/YogaBaselineFunction';
import { YogaConstants } from '../../yoga/YogaConstants';
import { UIViewOperationQueue } from './UIViewOperationQueue';
import { NativeViewHierarchyOptimizer } from './NativeViewHierarchyOptimizer';
import { Assertions } from '../inner/Assertions';
import { YogaNode } from '../../yoga/YogaNode';
import { Spacing } from './Spacing';
import { ThemedReactContext } from './ThemedReactContext';
import { ReactShadowNode } from './ReactShadowNode';
import { NativeKind } from './NativeKind';
import { YogaOverflow } from '../../yoga/YogaOverflow';
import { YogaValue } from '../../yoga/YogaValue';
import { YogaPositionType } from '../../yoga/YogaPositionType';
import { YogaFlexDirection } from '../../yoga/YogaFlexDirection';
import { YogaAlign } from '../../yoga/YogaAlign';
import { YogaDirection } from '../../yoga/YogaDirection';
import { getYogaDirectionFromNumber } from '../../yoga/YogaDirection';
import { YogaJustify } from '../../yoga/YogaJustify';
import { YogaConfig } from '../../yoga/YogaConfig';
import { YogaEdge } from '../../yoga/YogaEdge';
import { YogaDisplay } from '../../yoga/YogaDisplay';
import { YogaWrap } from '../../yoga/YogaWrap';
/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */


/**
 * Base node class for representing virtual tree of React nodes. Shadow nodes are used primarily for
 * layouting therefore it extends {@link YogaNode} to allow that. They also help with handling
 * Common base subclass of {@link YogaNode} for all layout nodes for react-based view. It extends
 * {@link YogaNode} by adding additional capabilities.
 *
 * <p>Instances of this class receive property updates from JS via @{link UIManagerModule}.
 * Subclasses may use {@link #updateShadowNode} to persist some of the updated fields in the node
 * instance that corresponds to a particular view type.
 *
 * <p>Subclasses of {@link ReactShadowNodeImpl} should be created only from {@link ViewManager} that
 * corresponds to a certain type of native view. They will be updated and accessed only from JS
 * thread. Subclasses of {@link ViewManager} may choose to use base class {@link
 * ReactShadowNodeImpl} or custom subclass of it if necessary.
 *
 * <p>The primary use-case for {@link ReactShadowNodeImpl} nodes is to calculate layouting. Although
 * this might be extended. For some examples please refer to ARTGroupYogaNode or ReactTextYogaNode.
 *
 * <p>This class allows for the native view hierarchy to not be an exact copy of the hierarchy
 * received from JS by keeping track of both JS children (e.g. {@link #getChildCount()} and
 * separately native children (e.g. {@link #getNativeChildCount()}). See {@link
 * NativeViewHierarchyOptimizer} for more information.
 */
//@ReactPropertyHolder

export class ReactShadowNodeImpl extends ReactShadowNode {

//  private static final YogaConfig sYogaConfig;
//
//  static {
//    sYogaConfig = ReactYogaConfigProvider.get();
//  }

  private mReactTag: number;
  private mViewType: string;
  private mRootTag: number;
  private mThemedContext: ThemedReactContext;
  private mShouldNotifyOnLayout: boolean;
  private mNodeUpdated = true;
  private mChildren?: ArrayList<ReactShadowNodeImpl>;
  private mParent?: ReactShadowNodeImpl;
  private mLayoutParent?: ReactShadowNodeImpl; // ????????????

  // layout-only nodes
  private mIsLayoutOnly: boolean;
  private mTotalNativeChildren = 0;
  private mNativeParent?: ReactShadowNodeImpl;
  private mNativeChildren?: ArrayList<ReactShadowNodeImpl>;
  private mScreenX: number;
  private mScreenY: number;
  private mScreenWidth: number;
  private mScreenHeight: number;
  private readonly mDefaultPadding: Spacing;
  private readonly mPadding: number[] = new Array<number>(Spacing.ALL + 1);
  private readonly mPaddingIsPercent: boolean[] = new Array<boolean>(Spacing.ALL + 1);
  private mYogaNode: YogaNode;
  private mWidthMeasureSpec: number; //Interger
  private mHeightMeasureSpec: number; //Interger

  public constructor() {
    super()
    this.mDefaultPadding = new Spacing(0);
    if (!this.isVirtual()) {
//      YogaNode node = YogaNodePool.get().acquire();
//      mYogaNode = node == null ? YogaNodeFactory.create(sYogaConfig) : node;
//      mYogaNode.setData(this);
//      Arrays.fill(mPadding, YogaConstants.UNDEFINED);
    } else {
      this.mYogaNode = null;
    }
  }

  /**
   * Nodes that return {@code true} will be treated as "virtual" nodes. That is, nodes that are not
   * mapped into native views or Yoga nodes (e.g. nested text node). By default this method returns
   * {@code false}.
   */
//  // @Override -> ts4.3 override
  public isVirtual() {
    return false;
  }

  /**
   * Nodes that return {@code true} will be treated as a root view for the virtual nodes tree. It
   * means that all of its descendants will be "virtual" nodes. Good example is {@code InputText}
   * view that may have children {@code Text} nodes but this whole hierarchy will be mapped to a
   * single android {@link EditText} view.
   */
//  // @Override -> ts4.3 override
  public isVirtualAnchor() {
    return false;
  }

  /**
   * Nodes that return {@code true} will not manage (and and remove) child Yoga nodes. For example
   * {@link ReactTextInputShadowNode} or {@link ReactTextShadowNode} have child nodes, which do not
   * want Yoga to lay out, so in the eyes of Yoga it is a leaf node. Override this method in
   * subclass to enforce this requirement.
   */
//  // @Override -> ts4.3 override
  public isYogaLeafNode(): boolean {
    return this.isMeasureDefined();
  }

  /**
   * When constructing the native tree, nodes that return {@code true} will be treated as leaves.
   * Instead of adding this view's native children as subviews of it, they will be added as subviews
   * of an ancestor. In other words, this view wants to support native children but it cannot host
   * them itself (e.g. it isn't a ViewGroup).
   */
//  // @Override -> ts4.3 override
  public hoistNativeChildren(): boolean {
    return false;
  }

//  // @Override -> ts4.3 override
  public getViewType(): string {
//    return Assertions.assertNotNull(this.mViewType);
      return this.mViewType;
  }

//  // @Override -> ts4.3 override
  public hasUpdates() {
    return this.mNodeUpdated || this.hasNewLayout() || this.isDirty();
  }

//  // @Override -> ts4.3 override
  public markUpdateSeen(): void {
    this.mNodeUpdated = false;
    if (this.hasNewLayout()) {
      this.markLayoutSeen();
    }
  }

//  // @Override -> ts4.3 override
  public markUpdated(): void {
    if (this.mNodeUpdated) {
      return;
    }
    this.mNodeUpdated = true;
    let parent: ReactShadowNodeImpl = this.getParent();
    if (parent != null) {
      parent.markUpdated();
    }
  }

//  // @Override -> ts4.3 override
  public hasUnseenUpdates(): boolean {
    return this.mNodeUpdated;
  }

//  // @Override -> ts4.3 override
  public dirty() {
    if (!this.isVirtual()) {
      this.mYogaNode.dirty();
    } else if (this.getParent() != null) {
      // Virtual nodes aren't involved in layout but they need to have the dirty signal
      // propagated to their ancestors.
      //
      // TODO: There are some edge cases that currently aren't supported. For example, if the size
      //   of your inline image/view changes, its size on-screen is not be updated. Similarly,
      //   if the size of a view inside of an inline view changes, its size on-screen is not
      //   updated. The problem may be that dirty propagation stops at inline views because the
      //   parent of each inline view is null. A possible fix would be to implement an `onDirty`
      //   handler in Yoga that will propagate the dirty signal to the ancestors of the inline view.
      //
      this.getParent().dirty();
    }
  }

//  // @Override -> ts4.3 override
  public isDirty(): boolean {
    return this.mYogaNode != null && this.mYogaNode.isDirty();
  }

//  // @Override -> ts4.3 override
  public addChildAt(child: ReactShadowNodeImpl, i: number): void {
    if (this.mChildren == null) {
      this.mChildren = new ArrayList<ReactShadowNodeImpl>();
      this.mChildren.increaseCapacityTo(4)
    }
    this.mChildren.insert(child, i);
    child.mParent = this;

    // If a CSS node has measure defined, the layout algorithm will not visit its children. Even
    // more, it asserts that you don't add children to nodes with measure functions.
    if (this.mYogaNode != null && !this.isYogaLeafNode()) {
      let childYogaNode: YogaNode = child.mYogaNode;
      if (childYogaNode == null) {
        throw new Error(
            "Cannot add a child that doesn't have a YogaNode to a parent without a measure "
                + "function! (Trying to add a '"
                + child.toString()
                + "' to a '"
                + this.toString()
                + "')");
      }
      this.mYogaNode.addChildAt(childYogaNode, i);
    }
    this.markUpdated();

    let increase: number = child.getTotalNativeNodeContributionToParent();
    this.mTotalNativeChildren += increase;

    this.updateNativeChildrenCountInParent(increase);
  }

  // @Override -> ts4.3 override
  public removeChildAt(i: number): ReactShadowNodeImpl {
    if (this.mChildren == null) {
      throw new Error(
          "Index " + i + " out of bounds: node has no children");
    }
    let removed: ReactShadowNodeImpl = this.mChildren.removeByIndex(i);
    removed.mParent = null;

    if (this.mYogaNode != null && !this.isYogaLeafNode()) {
      this.mYogaNode.removeChildAt(i);
    }
    this.markUpdated();

    let decrease: number = removed.getTotalNativeNodeContributionToParent();
    this.mTotalNativeChildren -= decrease;
    this.updateNativeChildrenCountInParent(-decrease);
    return removed;
  }

  // @Override -> ts4.3 override
  public getChildCount(): number {
    return this.mChildren == null ? 0 : this.mChildren.length;
  }

  // @Override -> ts4.3 override
  public getChildAt(i: number): ReactShadowNodeImpl {
    if (this.mChildren == null) {
      throw new Error(
          "Index " + i + " out of bounds: node has no children");
    }
    return this.mChildren[i];
  }

  // @Override -> ts4.3 override
  public indexOf(child: ReactShadowNodeImpl): number {
    return this.mChildren == null ? -1 : this.mChildren.getIndexOf(child);
  }

  // @Override -> ts4.3 override
  public removeAndDisposeAllChildren(): void {
    if (this.getChildCount() == 0) {
      return;
    }

    let decrease = 0;
    for (let i = this.getChildCount() - 1; i >= 0; i--) {
      if (this.mYogaNode != null && !this.isYogaLeafNode()) {
        this.mYogaNode.removeChildAt(i);
      }
      let toRemove: ReactShadowNodeImpl = this.getChildAt(i);
      toRemove.mParent = null;
      decrease += toRemove.getTotalNativeNodeContributionToParent();
      toRemove.dispose();
    }
//    Assertions.assertNotNull(mChildren).clear();
    this.mChildren.clear()
    this.markUpdated();

    this.mTotalNativeChildren -= decrease;
    this.updateNativeChildrenCountInParent(-decrease);
  }

  private updateNativeChildrenCountInParent(delta: number): void {
    if (this.getNativeKind() != NativeKind.PARENT) {
      let parent: ReactShadowNodeImpl = this.getParent();
      while (parent != null) {
        parent.mTotalNativeChildren += delta;
        if (parent.getNativeKind() == NativeKind.PARENT) {
          break;
        }
        parent = parent.getParent();
      }
    }
  }

  /**
   * This method will be called by {@link UIManagerModule} once per batch, before calculating
   * layout. Will be only called for nodes that are marked as updated with {@link #markUpdated()} or
   * require layouting (marked with {@link #dirty()}).
   */
  // @Override -> ts4.3 override
  public onBeforeLayout(nativeViewHierarchyOptimizer: NativeViewHierarchyOptimizer): void {}

  // @Override -> ts4.3 override
  public updateProperties(props: ReactStylesDiffMap): void {
    //todo:  ViewManagerPropertyUpdater
    ViewManagerPropertyUpdater.updateProps(this, props);
    this.onAfterUpdateTransaction();
  }

  // @Override -> ts4.3 override
  public onAfterUpdateTransaction(): void {
    // no-op
  }

  /**
   * Called after layout step at the end of the UI batch from {@link UIManagerModule}. May be used
   * to enqueue additional ui operations for the native view. Will only be called on nodes marked as
   * updated either with {@link #dirty()} or {@link #markUpdated()}.
   *
   * @param uiViewOperationQueue interface for enqueueing UI operations
   */
  // @Override -> ts4.3 override
  public onCollectExtraUpdates(uiViewOperationQueue: UIViewOperationQueue): void {}

  /** @return true if layout (position or dimensions) changed, false otherwise. */
  // @Override -> ts4.3 override
  public dispatchUpdates(
      absoluteX: number,
      absoluteY: number,
      uiViewOperationQueue: UIViewOperationQueue,
      nativeViewHierarchyOptimizer?:NativeViewHierarchyOptimizer): boolean {
    if (this.mNodeUpdated) {
      this.onCollectExtraUpdates(uiViewOperationQueue);
    }

    if (this.hasNewLayout()) {
      let layoutX = this.getLayoutX();
      let layoutY = this.getLayoutY();
      let newAbsoluteLeft = Math.round(absoluteX + layoutX);
      let newAbsoluteTop = Math.round(absoluteY + layoutY);
      let newAbsoluteRight = Math.round(absoluteX + layoutX + this.getLayoutWidth());
      let newAbsoluteBottom = Math.round(absoluteY + layoutY + this.getLayoutHeight());

      let newScreenX = Math.round(layoutX);
      let newScreenY = Math.round(layoutY);
      let newScreenWidth = newAbsoluteRight - newAbsoluteLeft;
      let newScreenHeight = newAbsoluteBottom - newAbsoluteTop;

      let layoutHasChanged: boolean =
          newScreenX != this.mScreenX
              || newScreenY != this.mScreenY
              || newScreenWidth != this.mScreenWidth
              || newScreenHeight != this.mScreenHeight;

      this.mScreenX = newScreenX;
      this.mScreenY = newScreenY;
      this.mScreenWidth = newScreenWidth;
      this.mScreenHeight = newScreenHeight;

      if (layoutHasChanged) {
        // TODO: T26400974 ReactShadowNode should not depend on nativeViewHierarchyOptimizer
        if (nativeViewHierarchyOptimizer != null) {
          nativeViewHierarchyOptimizer.handleUpdateLayout(this);
        } else {
          uiViewOperationQueue.enqueueUpdateLayout(
              this.getParent().getReactTag(),
              this.getReactTag(),
              this.getScreenX(),
              this.getScreenY(),
              this.getScreenWidth(),
              this.getScreenHeight());
        }
      }

      return layoutHasChanged;
    } else {
      return false;
    }
  }

  // @Override -> ts4.3 override
  public getReactTag(): number {
    return this.mReactTag;
  }

  // @Override -> ts4.3 override
  public setReactTag(reactTag: number): void {
    this.mReactTag = reactTag;
  }

  // @Override -> ts4.3 override
  public getRootTag(): number {
//    Assertions.assertCondition(mRootTag != 0);
    if (this.mRootTag == 0) throw Error("mRootTag should not be null")
    return this.mRootTag;
  }

  // @Override -> ts4.3 override
  public setRootTag(rootTag: number): void {
    this.mRootTag = rootTag;
  }

  // @Override -> ts4.3 override
  public setViewTypeName(viewClassName: string): void {
     this.mViewType = viewClassName;
  }

  // @Override -> ts4.3 override
  public getParent(): ReactShadowNodeImpl | null {
    return this.mParent;
  }

  // Returns the node that is responsible for laying out this node.
  // @Override -> ts4.3 override
  public getLayoutParent(): ReactShadowNodeImpl | null {
    return this.mLayoutParent != null ? this.mLayoutParent : this.getNativeParent();
  }

  // @Override -> ts4.3 override
  public setLayoutParent(layoutParent?: ReactShadowNodeImpl): void {
    this.mLayoutParent = layoutParent;
  }

  /**
   * Get the {@link ThemedReactContext} associated with this {@link ReactShadowNodeImpl}. This will
   * never change during the lifetime of a {@link ReactShadowNodeImpl} instance, but different
   * instances can have different contexts; don't cache any calculations based on theme values
   * globally.
   */
  // @Override -> ts4.3 override
  public getThemedContext(): ThemedReactContext {
    return this.mThemedContext
//    return Assertions.assertNotNull(mThemedContext);
  }

  // @Override -> ts4.3 override
  public setThemedContext(themedContext: ThemedReactContext) {
    this.mThemedContext = themedContext;
  }

  // @Override -> ts4.3 override
  public shouldNotifyOnLayout(): boolean {
    return this.mShouldNotifyOnLayout;
  }

  // @Override -> ts4.3 override
//  public calculateLayout() {
//    calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
//  }

  // @Override -> ts4.3 override
  public calculateLayout(width: number = YogaConstants.UNDEFINED , height: number = YogaConstants.UNDEFINED) {
    this.mYogaNode.calculateLayout(width, height);
  }

  // @Override -> ts4.3 override
  public hasNewLayout(): boolean {
    return this.mYogaNode != null && this.mYogaNode.hasNewLayout();
  }

  // @Override -> ts4.3 override
  public markLayoutSeen(): void {
    if (this.mYogaNode != null) {
      this.mYogaNode.markLayoutSeen();
    }
  }

  /**
   * Adds a child that the native view hierarchy will have at this index in the native view
   * corresponding to this node.
   */
  // @Override -> ts4.3 override
  public addNativeChildAt(child: ReactShadowNodeImpl, nativeIndex: number): void {
    Assertions.assertCondition(this.getNativeKind() == NativeKind.PARENT);
    Assertions.assertCondition(child.getNativeKind() != NativeKind.NONE);

    if (this.mNativeChildren == null) {
      this.mNativeChildren = new ArrayList<ReactShadowNodeImpl>();
      this.mNativeChildren.increaseCapacityTo(4)
    }
    this.mNativeChildren.insert(child, nativeIndex)
    child.mNativeParent = this;
  }

  // @Override -> ts4.3 override
  public removeNativeChildAt(i: number): ReactShadowNodeImpl {
    Assertions.assertNotNull(this.mNativeChildren != null);
    let removed: ReactShadowNodeImpl = this.mNativeChildren.removeByIndex(i);
    removed.mNativeParent = null;
    return removed;
  }

  // @Override -> ts4.3 override
  public removeAllNativeChildren(): void  {
    if (this.mNativeChildren != null) {
      for (let i = this.mNativeChildren.length - 1; i >= 0; i--) {
         this.mNativeChildren[i].mNativeParent = null;
      }
      this.mNativeChildren.clear();
    }
  }

  // @Override -> ts4.3 override
  public getNativeChildCount(): number  {
    return this.mNativeChildren == null ? 0 : this.mNativeChildren.length;
  }

  // @Override -> ts4.3 override
  public indexOfNativeChild(nativeChild: ReactShadowNodeImpl): number {
    Assertions.assertNotNull(this.mNativeChildren != null);
    return this.mNativeChildren.getIndexOf(nativeChild);
  }

  // @Override -> ts4.3 override
  public getNativeParent():  ReactShadowNodeImpl | null {
    return this.mNativeParent;
  }

  /**
   * Sets whether this node only contributes to the layout of its children without doing any drawing
   * or functionality itself.
   */
  // @Override -> ts4.3 override
  public setIsLayoutOnly(isLayoutOnly: boolean): void {
    Assertions.assertCondition(this.getParent() == null, "Must remove from no opt parent first");
    Assertions.assertCondition(this.mNativeParent == null, "Must remove from native parent first");
    Assertions.assertCondition(this.getNativeChildCount() == 0, "Must remove all native children first");
    this.mIsLayoutOnly = isLayoutOnly;
  }

  // @Override -> ts4.3 override
  public isLayoutOnly(): boolean {
    return this.mIsLayoutOnly;
  }

  // @Override -> ts4.3 override
  public getNativeKind(): NativeKind {
    return this.isVirtual() || this.isLayoutOnly()
        ? NativeKind.NONE
        : this.hoistNativeChildren() ? NativeKind.LEAF : NativeKind.PARENT;
  }

  // @Override -> ts4.3 override
  public getTotalNativeChildren(): number {
    return this.mTotalNativeChildren;
  }

  // @Override -> ts4.3 override
  public isDescendantOf(ancestorNode: ReactShadowNodeImpl): boolean {
    let parentNode: ReactShadowNodeImpl = this.getParent();

    let isDescendant = false;

    while (parentNode != null) {
      if (parentNode == ancestorNode) {
        isDescendant = true;
        break;
      } else {
        parentNode = parentNode.getParent();
      }
    }

    return isDescendant;
  }

  private getTotalNativeNodeContributionToParent(): number {
    let kind: NativeKind = this.getNativeKind();
    return kind == NativeKind.NONE
        ? this.mTotalNativeChildren
        : kind == NativeKind.LEAF ? 1 + this.mTotalNativeChildren : 1; // kind == NativeKind.PARENT
  }

  // @Override -> ts4.3 override
  public toString(): string {
    return "[" +  this.mViewType + " " + this.getReactTag() + "]";
  }

  /*
   * In some cases we need a way to specify some environmental data to shadow node
   * to improve layout (or do something similar), so {@code localData} serves these needs.
   * For example, any stateful embedded native views may benefit from this.
   * Have in mind that this data is not supposed to interfere with the state of
   * the shadow node.
   * Please respect one-directional data flow of React.
   * Use  {@link ReactUIManagerModule#setViewLocalData} to set this property
   * (to provide local/environmental data for a shadow node) from the main thread.
   */
  public setLocalData(data: Object): void {}

  /**
   * Returns the offset within the native children owned by all layout-only nodes in the subtree
   * rooted at this node for the given child. Put another way, this returns the number of native
   * nodes (nodes not optimized out of the native tree) that are a) to the left (visited before by a
   * DFS) of the given child in the subtree rooted at this node and b) do not have a native parent
   * in this subtree (which means that the given child will be a sibling of theirs in the final
   * native hierarchy since they'll get attached to the same native parent).
   *
   * <p>Basically, a view might have children that have been optimized away by {@link
   * NativeViewHierarchyOptimizer}. Since those children will then add their native children to this
   * view, we now have ranges of native children that correspond to single unoptimized children. The
   * purpose of this method is to return the index within the native children that corresponds to
   * the **start** of the native children that belong to the given child. Also, note that all of the
   * children of a view might be optimized away, so this could return the same value for multiple
   * different children.
   *
   * <p>Example. Native children are represented by (N) where N is the no-opt child they came from.
   * If no children are optimized away it'd look like this: (0) (1) (2) (3) ... (n)
   *
   * <p>In case some children are optimized away, it might look like this: (0) (1) (1) (1) (3) (3)
   * (4)
   *
   * <p>In that case: getNativeOffsetForChild(Node 0) => 0 getNativeOffsetForChild(Node 1) => 1
   * getNativeOffsetForChild(Node 2) => 4 getNativeOffsetForChild(Node 3) => 4
   *
   * <p>getNativeOffsetForChild(Node 4) => 6
   */
  // @Override -> ts4.3 override
  public getNativeOffsetForChild(child: ReactShadowNodeImpl): number {
    let index = 0;
    let found = false;
    for (let i = 0; i < this.getChildCount(); i++) {
      let current: ReactShadowNodeImpl = this.getChildAt(i);
      if (child == current) {
        found = true;
        break;
      }
      index += current.getTotalNativeNodeContributionToParent();
    }
    if (!found) {
      throw new Error(
          "Child " + child.getReactTag() + " was not a child of " + this.mReactTag);
    }
    return index;
  }

  // @Override -> ts4.3 override
  public getLayoutX(): number {
    return this.mYogaNode.getLayoutX();
  }

  // @Override -> ts4.3 override
  public getLayoutY(): number  {
    return this.mYogaNode.getLayoutY();
  }

  // @Override -> ts4.3 override
  public getLayoutWidth(): number  {
    return this.mYogaNode.getLayoutWidth();
  }

  // @Override -> ts4.3 override
  public getLayoutHeight(): number  {
    return this.mYogaNode.getLayoutHeight();
  }

  /** @return the x position of the corresponding view on the screen, rounded to pixels */
  // @Override -> ts4.3 override
  public getScreenX(): number {
    return this.mScreenX;
  }

  /** @return the y position of the corresponding view on the screen, rounded to pixels */
  // @Override -> ts4.3 override
  public getScreenY(): number {
    return this.mScreenY;
  }

  /** @return width corrected for rounding to pixels. */
  // @Override -> ts4.3 override
  public getScreenWidth() {
    return this.mScreenWidth;
  }

  /** @return height corrected for rounding to pixels. */
  // @Override -> ts4.3 override
  public getScreenHeight() {
    return this.mScreenHeight;
  }

  // @Override -> ts4.3 override
  public getLayoutDirection(): YogaDirection {
    return this.mYogaNode.getLayoutDirection();
  }

  // @Override -> ts4.3 override
  public setLayoutDirection(direction: YogaDirection) {
    this.mYogaNode.setDirection(direction);
  }

  // @Override -> ts4.3 override
  public getStyleWidth(): YogaValue {
    return this.mYogaNode.getWidth();
  }

  // @Override -> ts4.3 override
  public setStyleWidth(widthPx: number) {
    this.mYogaNode.setWidth(widthPx);
  }

  // @Override -> ts4.3 override
  public setStyleWidthPercent(percent: number) {
    this.mYogaNode.setWidthPercent(percent);
  }

  // @Override -> ts4.3 override
  public setStyleWidthAuto() {
    this.mYogaNode.setWidthAuto();
  }

  // @Override -> ts4.3 override
  public setStyleMinWidth(widthPx: number) {
    this.mYogaNode.setMinWidth(widthPx);
  }

  // @Override -> ts4.3 override
  public setStyleMinWidthPercent(percent: number) {
    this.mYogaNode.setMinWidthPercent(percent);
  }

  // @Override -> ts4.3 override
  public setStyleMaxWidth(widthPx: number) {
    this.mYogaNode.setMaxWidth(widthPx);
  }

  // @Override -> ts4.3 override
  public setStyleMaxWidthPercent(percent: number) {
    this.mYogaNode.setMaxWidthPercent(percent);
  }

  // @Override -> ts4.3 override
  public getStyleHeight(): YogaValue {
    return this.mYogaNode.getHeight();
  }

  // @Override -> ts4.3 override
  public setStyleHeight(heightPx: number) {
    this.mYogaNode.setHeight(heightPx);
  }

  // @Override -> ts4.3 override
  public setStyleHeightPercent(percent: number) {
    this.mYogaNode.setHeightPercent(percent);
  }

  // @Override -> ts4.3 override
  public setStyleHeightAuto() {
    this.mYogaNode.setHeightAuto();
  }

  // @Override -> ts4.3 override
  public setStyleMinHeight(widthPx: number) {
    this.mYogaNode.setMinHeight(widthPx);
  }

  // @Override -> ts4.3 override
  public setStyleMinHeightPercent(percent: number) {
    this.mYogaNode.setMinHeightPercent(percent);
  }

  // @Override -> ts4.3 override
  public setStyleMaxHeight(widthPx: number) {
    this.mYogaNode.setMaxHeight(widthPx);
  }

  // @Override -> ts4.3 override
  public setStyleMaxHeightPercent(percent: number) {
    this.mYogaNode.setMaxHeightPercent(percent);
  }

  // @Override -> ts4.3 override
  public getFlex(): number {
    return this.mYogaNode.getFlex();
  }

  // @Override -> ts4.3 override
  public setFlex(flex: number) {
    this.mYogaNode.setFlex(flex);
  }

  // @Override -> ts4.3 override
  public setFlexGrow(flexGrow: number) {
    this.mYogaNode.setFlexGrow(flexGrow);
  }

  // @Override -> ts4.3 override
  public setFlexShrink(flexShrink: number) {
    this.mYogaNode.setFlexShrink(flexShrink);
  }

  // @Override -> ts4.3 override
  public setFlexBasis(flexBasis: number) {
    this.mYogaNode.setFlexBasis(flexBasis);
  }

  // @Override -> ts4.3 override
  public setFlexBasisAuto() {
    this.mYogaNode.setFlexBasisAuto();
  }

  // @Override -> ts4.3 override
  public setFlexBasisPercent(percent: number) {
    this.mYogaNode.setFlexBasisPercent(percent);
  }

  // @Override -> ts4.3 override
  public setStyleAspectRatio(aspectRatio: number) {
    this.mYogaNode.setAspectRatio(aspectRatio);
  }

  // @Override -> ts4.3 override
  public setFlexDirection(flexDirection: YogaFlexDirection) {
    this.mYogaNode.setFlexDirection(flexDirection);
  }

  // @Override -> ts4.3 override
  public setFlexWrap(wrap: YogaWrap) {
    this.mYogaNode.setWrap(wrap);
  }

  // @Override -> ts4.3 override
  public setAlignSelf(alignSelf: YogaAlign) {
    this.mYogaNode.setAlignSelf(alignSelf);
  }

  // @Override -> ts4.3 override
  public setAlignItems(alignItems: YogaAlign) {
    this.mYogaNode.setAlignItems(alignItems);
  }

  // @Override -> ts4.3 override
  public setAlignContent(alignContent: YogaAlign) {
    this.mYogaNode.setAlignContent(alignContent);
  }

  // @Override -> ts4.3 override
  public setJustifyContent(justifyContent: YogaJustify) {
    this.mYogaNode.setJustifyContent(justifyContent);
  }

  // @Override -> ts4.3 override
  public setOverflow(overflow: YogaOverflow) {
    this.mYogaNode.setOverflow(overflow);
  }

  // @Override -> ts4.3 override
  public setDisplay(display: YogaDisplay) {
    this.mYogaNode.setDisplay(display);
  }

  // @Override -> ts4.3 override
  public setMargin(spacingType: number, margin: number) {
    this.mYogaNode.setMargin(getYogaEdgeFromNumber(spacingType), margin);
  }

  // @Override -> ts4.3 override
  public setMarginPercent(spacingType: number, percent: number) {
    this.mYogaNode.setMarginPercent(getYogaEdgeFromNumber(spacingType), percent);
  }

  // @Override -> ts4.3 override
  public setMarginAuto(spacingType: number) {
    this.mYogaNode.setMarginAuto(getYogaEdgeFromNumber(spacingType));
  }

  // @Override -> ts4.3 override
  public getPadding(spacingType: number): number {
    return this.mYogaNode.getLayoutPadding(getYogaEdgeFromNumber(spacingType));
  }

  // @Override -> ts4.3 override
  public getStylePadding(spacingType: number): YogaValue {
    return this.mYogaNode.getPadding(getYogaEdgeFromNumber(spacingType));
  }

  // @Override -> ts4.3 override
  public setDefaultPadding(spacingType: number, padding: number) {
    this.mDefaultPadding.set(spacingType, padding);
    this.updatePadding();
  }

  // @Override - > ts 4.3 override
  public setPadding(spacingType: number, padding: number) {
    this.mPadding[spacingType] = padding;
    this.mPaddingIsPercent[spacingType] = false;
    this.updatePadding();
  }

  // @Override - > ts 4.3 override
  public setPaddingPercent(spacingType: number, percent: number) {
    this.mPadding[spacingType] = percent;
    this.mPaddingIsPercent[spacingType] = !YogaConstants.isUndefined(percent);
    this.updatePadding();
  }

  private updatePadding() {
    for (let spacingType = Spacing.LEFT; spacingType <= Spacing.ALL; spacingType++) {
      if (spacingType == Spacing.LEFT
          || spacingType == Spacing.RIGHT
          || spacingType == Spacing.START
          || spacingType == Spacing.END) {
        if (YogaConstants.isUndefined(this.mPadding[spacingType])
            && YogaConstants.isUndefined(this.mPadding[Spacing.HORIZONTAL])
            && YogaConstants.isUndefined(this.mPadding[Spacing.ALL])) {
          this.mYogaNode.setPadding(getYogaEdgeFromNumber(spacingType), this.mDefaultPadding.getRaw(spacingType));
          continue;
        }
      } else if (spacingType == Spacing.TOP || spacingType == Spacing.BOTTOM) {
        if (YogaConstants.isUndefined(this.mPadding[spacingType])
            && YogaConstants.isUndefined(this.mPadding[Spacing.VERTICAL])
            && YogaConstants.isUndefined(this.mPadding[Spacing.ALL])) {
          this.mYogaNode.setPadding(getYogaEdgeFromNumber(spacingType), this.mDefaultPadding.getRaw(spacingType));
          continue;
        }
      } else {
        if (YogaConstants.isUndefined(this.mPadding[spacingType])) {
          this.mYogaNode.setPadding(getYogaEdgeFromNumber(spacingType), this.mDefaultPadding.getRaw(spacingType));
          continue;
        }
      }

      if (this.mPaddingIsPercent[spacingType]) {
        this.mYogaNode.setPaddingPercent(getYogaEdgeFromNumber(spacingType), this.mPadding[spacingType]);
      } else {
        this.mYogaNode.setPadding(getYogaEdgeFromNumber(spacingType), this.mPadding[spacingType]);
      }
    }
  }

  // @Override - > ts 4.3 override
  public setBorder(spacingType: number, borderWidth: number) {
    this.mYogaNode.setBorder(getYogaEdgeFromNumber(spacingType), borderWidth);
  }

  // @Override - > ts 4.3 override
  public setPosition(spacingType: number, position: number) {
    this.mYogaNode.setPosition(getYogaEdgeFromNumber(spacingType), position);
  }

  // @Override - > ts 4.3 override
  public setPositionPercent(spacingType: number, percent: number) {
    this.mYogaNode.setPositionPercent(getYogaEdgeFromNumber(spacingType), percent);
  }

  // @Override - > ts 4.3 override
  public setPositionType(positionType: YogaPositionType) {
    this.mYogaNode.setPositionType(positionType);
  }

  // @Override - > ts 4.3 override
  public setShouldNotifyOnLayout(shouldNotifyOnLayout: boolean) {
    this.mShouldNotifyOnLayout = shouldNotifyOnLayout;
  }

  // @Override - > ts 4.3 override
  public setBaselineFunction(baselineFunction: YogaBaselineFunction) {
    this.mYogaNode.setBaselineFunction(baselineFunction);
  }

  // @Override - > ts 4.3 override
  public setMeasureFunction(measureFunction: YogaMeasureFunction) {
    this.mYogaNode.setMeasureFunction(measureFunction);
  }

//  // @Override - > ts 4.3 override
  public isMeasureDefined(): boolean {
    return this.mYogaNode.isMeasureDefined();
  }

  // @Override - > ts 4.3 override
  public getHierarchyInfo(): string {
      return ""
//    StringBuilder sb = new StringBuilder();
//    this.getHierarchyInfoWithIndentation(sb, 0);
//    return sb.toString();
  }

//  private getHierarchyInfoWithIndentation(StringBuilder result, int level): void {
//    // Spaces and tabs are dropped by IntelliJ logcat integration, so rely on __ instead.
//    for (int i = 0; i < level; ++i) {
//      result.append("  ");
//    }
//
//    result
//        .append("<")
//        .append(getClass().getSimpleName())
//        .append(" view='")
//        .append(getViewClass())
//        .append("' tag=")
//        .append(getReactTag());
//    if (this.mYogaNode != null) {
//      result
//          .append(" layout='x:")
//          .append(getScreenX())
//          .append(" y:")
//          .append(getScreenY())
//          .append(" w:")
//          .append(getLayoutWidth())
//          .append(" h:")
//          .append(getLayoutHeight())
//          .append("'");
//    } else {
//      result.append("(virtual node)");
//    }
//    result.append(">\n");
//
//    if (getChildCount() == 0) {
//      return;
//    }
//
//    for (let i = 0; i < this.getChildCount(); i++) {
//      this.getChildAt(i).getHierarchyInfoWithIndentation(result, level + 1);
//    }
//  }

  // @Override - > ts 4.3 override
  public dispose() {
    if (this.mYogaNode != null) {
      this.mYogaNode.reset();
      // todo: YogaNodePool
      //YogaNodePool.get().release(this.mYogaNode);
    }
  }

  // @Override - > ts 4.3 override
  public setMeasureSpecs(widthMeasureSpec: number, heightMeasureSpec: number) {
    this.mWidthMeasureSpec = widthMeasureSpec;
    this.mHeightMeasureSpec = heightMeasureSpec;
  }

  // @Override - > ts 4.3 override
  public getWidthMeasureSpec(): number { // return Integer
    return this.mWidthMeasureSpec;
  }

  // @Override - > ts 4.3 override
  public getHeightMeasureSpec(): number { // return Integer
    return this.mHeightMeasureSpec;
  }

  // @Override - > ts 4.3 override
//  public Iterable<? extends ReactShadowNode> calculateLayoutOnChildren() {
    public calculateLayoutOnChildren(): IterableIterator<ReactShadowNodeImpl> | null {
    return this.isVirtualAnchor()
        ?
        // All of the descendants are virtual so none of them are involved in layout.
        null
        :
        // Just return the children. Flexbox calculations have already been run on them.
        this.mChildren[Symbol.iterator]();
  }
}
